package com.cloudkinto.service.role.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.common.RedisKeyConstants;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.dict.StaticDict;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.dao.MenuDao;
import com.cloudkinto.dao.RoleDao;
import com.cloudkinto.dao.RoleMenuRelationDao;
import com.cloudkinto.dao.UserDao;
import com.cloudkinto.vo.menu.MenuVo;
import com.cloudkinto.vo.menu.MenuRelationVo;
import com.cloudkinto.vo.menu.RolePageVo;
import com.cloudkinto.entity.MenuDo;
import com.cloudkinto.entity.RoleDo;
import com.cloudkinto.entity.RoleMenuRelationDo;
import com.cloudkinto.entity.UserDo;
import com.cloudkinto.service.role.RoleService;
import com.cloudkinto.vo.role.menu.MenuInitRes;
import com.cloudkinto.vo.role.menu.MenuNewTreeRes;
import com.cloudkinto.vo.role.menu.MetaTreeRes;
import com.cloudkinto.vo.role.role.*;
import com.cloudkinto.vo.role.role.omp.RoleOmpAddReq;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * service实现类
 * </p>
 *
 * @author 张永远
 * @since 2020-05-29
 */
@Service
@Transactional(rollbackFor = Exception.class)//事务回滚
public class RoleServiceImpl extends ServiceImpl<RoleDao, RoleDo> implements RoleService {
    @Autowired
    private RoleDao dao;
    @Autowired
    private RoleMenuRelationDao roleMenuRelationDao;
    @Autowired
    private MenuDao menuDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private RedisTemplate redisTemplate;

    public List<String> getButton(Long roleId, Integer system) {
        Object o = redisTemplate.opsForValue().get(RedisKeyConstants.roleButtonRedisKey + system + "_" + roleId);
        if (o == null) {
            List<MenuVo> button = menuDao.getButton(roleId, Collections.singletonList(system), 3);
            List<String> collect = button.stream().map(MenuVo::getUrl).collect(Collectors.toList());
            redisTemplate.opsForValue().set(RedisKeyConstants.roleButtonRedisKey+ system + "_" + roleId, JSON.toJSONString(collect), 1000 * 60 * 60 * 2, TimeUnit.MILLISECONDS);
            return collect;
        } else {
            return JSON.parseArray(o.toString(), String.class);
        }
    }


    @Override
    public void add(RoleAddReq req, Long userId, Long companyId, String source) {
        RoleDo entityDo = new RoleDo();
        if ("omp".equals(source)) {
            entityDo.setRoleType(StaticDict.System_RoleType.Labella_New_Back.getValue());
            companyId = null;
        } else if ("oms".equals(source)) {
            entityDo.setRoleType(StaticDict.System_RoleType.Labella_New_Cus.getValue());
            UserDo userDo = userDao.selectById(userId);
            if (userDo == null) return;
            entityDo.setTenantId(userDo.getTenantId());
        } else {
            return;
        }
        addCheck(req, companyId, entityDo.getTenantId(), entityDo.getRoleType());
        entityDo.setRoleName(req.getRoleName());
        entityDo.setCompanyId(companyId);
        entityDo.setTips(req.getTips());
        dao.insert(entityDo);
        //菜单
        this.dealMenu(req, source, entityDo);
    }

    public void dealMenu(RoleAddReq req, String source, RoleDo entityDo) {
        List<Long> menuIds = new ArrayList<>();
        if ("oms".equals(source)) {
            menuIds.addAll(req.getOmsMenuIds());
        } else {
            menuIds.addAll(req.getOmpMenuIds());
            menuIds.addAll(req.getWmsMenuIds());
        }
        this.giveMenu(new RoleGiveMenuReq(entityDo.getId(), menuIds), source);
    }

    @Override
    public void giveMenu(RoleGiveMenuReq req, String source) {
        if (req.getRoleId() == null) return;
        List<Integer> systems = StaticDict.System_MenuType.getMenuTypeForInit(source);
        if (systems == null) return;
        List<MenuRelationVo> ruleRelationDtos = menuDao.getMenuByRole(req.getRoleId());
        List<Long> deleteIds = ruleRelationDtos.stream().filter(i -> i.getMenuId() == null || !systems.contains(i.getSystem_()) || !req.getMenuIds().contains(i.getMenuId())).map(MenuRelationVo::getId).collect(Collectors.toList());
        if (!deleteIds.isEmpty()) {
            roleMenuRelationDao.deleteBatchIds(deleteIds);
        }
        Set<Long> menuIds = ruleRelationDtos.stream().filter(i -> i.getMenuId() != null && systems.contains(i.getSystem_()) && req.getMenuIds().contains(i.getMenuId())).map(MenuRelationVo::getMenuId).collect(Collectors.toSet());
        Set<Long> reqMenuId = new HashSet<>(req.getMenuIds());
        //查询所有呗 隐藏的 菜单
//        List<MenuDo> hiddenMenu = menuDao.selectList(new LambdaQueryWrapper<MenuDo>().eq(MenuDo::getHidden, true));
//        if (hiddenMenu.size() > 0) {
//            reqMenuId.addAll(hiddenMenu.stream().map(MenuDo::getId).collect(Collectors.toSet()));
//        }
        for (Long menuId : reqMenuId) {
            //判断需不需要插入
            if (menuIds.contains(menuId)) {
                continue;
            }
            RoleMenuRelationDo relationDo = new RoleMenuRelationDo(req.getRoleId(), menuId);
            roleMenuRelationDao.insert(relationDo);
        }
        List<MenuVo> roleButton = menuDao.getButton(req.getRoleId(), systems, 3);
        Map<Integer, List<MenuVo>> collect = roleButton.stream().collect(Collectors.groupingBy(MenuVo::getSystem_));
        for (Integer system : collect.keySet()) {
            //2 小时
            redisTemplate.opsForValue().set(RedisKeyConstants.roleButtonRedisKey + system + "_" + req.getRoleId(), JSON.toJSONString(roleButton), 1000 * 60 * 60 * 2, TimeUnit.MILLISECONDS);
        }
    }

    public Set<Long> getAllMenuForAdmin(Long roleId, List<Integer> system) {
        Set<Long> menuSet = new HashSet<>();
        if (system == null || system.isEmpty()) return menuSet;
        RoleDo roleDo = dao.selectById(roleId);
        if (roleDo == null) {
            return menuSet;
        }
        if (SysConstant.clientAdmin.equals(roleDo.getDevCode())) {
            LambdaQueryWrapper<MenuDo> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(MenuDo::getSystem, system).eq(MenuDo::getHidden, false).orderByAsc(MenuDo::getSort);
            List<MenuDo> menuDoList = menuDao.selectList(wrapper);
            for (MenuDo menuDo : menuDoList) {
                menuSet.add(menuDo.getId());
            }
        } else {
            List<MenuVo> button = menuDao.getButton(roleId, system, null);
            menuSet = button.stream().map(MenuVo::getId).collect(Collectors.toSet());
        }
        return menuSet;
    }


    @Override
    public MenuInitRes giveMenuInit(String source) {
        List<Integer> system = StaticDict.System_MenuType.getMenuTypeForInit(source);
        if (system == null) return null;
        MenuInitRes res = new MenuInitRes();
        for (Integer i : system) {
            List<RoleGiveMenuInitTree> menuTreeInit = this.getMenuTreeInit(Arrays.asList(i), null);
            if (StaticDict.System_MenuType.Labella_Oms.getValue().equals(i)) {
                res.setOmsMenuList(menuTreeInit);
            } else if (StaticDict.System_MenuType.Labella_Omp.getValue().equals(i)) {
                res.setOmpMenuList(menuTreeInit);
            } else if (StaticDict.System_MenuType.Labella_Wms.getValue().equals(i)) {
                res.setWmsMenuList(menuTreeInit);
            }
        }


        return res;
    }


    public List<RoleGiveMenuInitTree> getMenuTreeInit(Long id, String source) {
        List<Integer> system = StaticDict.System_MenuType.getMenuTypeForInit(source);
        if (system == null || system.isEmpty()) return null;
        Set<Long> menuSet = this.getAllMenuForAdmin(id, system);
        return this.getMenuTreeInit(system, menuSet);
    }

    public List<RoleGiveMenuInitTree> getMenuTreeInit(List<Integer> system, Set<Long> menuSet) {
        List<RoleGiveMenuInitTree> treeList = new ArrayList<>();
        LambdaQueryWrapper<MenuDo> menuWrapper = new LambdaQueryWrapper<>();
        menuWrapper.eq(MenuDo::getParentId, 0L).eq(MenuDo::getHidden, false).in(MenuDo::getSystem, system).orderByAsc(MenuDo::getSort);
        List<MenuDo> menuDoList = menuDao.selectList(menuWrapper);
        for (MenuDo menuDo : menuDoList) {
            RoleGiveMenuInitTree tree = new RoleGiveMenuInitTree(menuDo.getId(), menuDo.getUrl(), menuDo.getComponent(), menuDo.getName(), 1, checkStatus(menuDo.getId(), menuSet), menuDo.getIcon(), menuDo.getSystem());
            tree.setMeta(new MetaTreeRes(menuDo.getName(), menuDo.getIcon()));
            //查询子菜单
            List<RoleGiveMenuInitTree> children = queryChildren(menuDo.getId(), menuSet, 2, system);
            if (children.isEmpty()) {
                //在筛选按钮
                children = this.queryButton(menuDo.getId(), menuSet, system);
            }
            tree.setChildren(children);
            treeList.add(tree);
        }
        return treeList;
    }

    private List<RoleGiveMenuInitTree> queryChildren(Long menuId, Set<Long> menuSet, int type, List<Integer> system) {
        if (menuId != null) {
            LambdaQueryWrapper<MenuDo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(MenuDo::getParentId, menuId).in(MenuDo::getSystem, system).eq(MenuDo::getHidden, false).eq(MenuDo::getType, type).orderByAsc(MenuDo::getSort);
            List<MenuDo> menuDoList = menuDao.selectList(wrapper);
            List<RoleGiveMenuInitTree> treeList = new ArrayList<>();
            for (MenuDo menuDo : menuDoList) {
                RoleGiveMenuInitTree tree = new RoleGiveMenuInitTree(menuDo.getId(), menuDo.getUrl(), menuDo.getComponent(), menuDo.getName(), menuDo.getType(), checkStatus(menuDo.getId(), menuSet), menuDo.getIcon(), menuDo.getSystem());
                tree.setMeta(new MetaTreeRes(menuDo.getName(), menuDo.getIcon()));
                List<RoleGiveMenuInitTree> children = queryChildren(menuDo.getId(), menuSet, type, system);
                if (children.isEmpty()) {
                    //在筛选按钮
                    children = this.queryButton(menuDo.getId(), menuSet, system);
                }
                tree.setChildren(children);
                treeList.add(tree);
            }
            return treeList;

        }
        return null;
    }


    public List<RoleGiveMenuInitTree> queryButton(Long menuId, Set<Long> menuSet, List<Integer> system) {
        //在筛选按钮
        LambdaQueryWrapper<MenuDo> buttonWra = new LambdaQueryWrapper<>();
        buttonWra.eq(MenuDo::getParentId, menuId).in(MenuDo::getSystem, system).eq(MenuDo::getType, 3).orderByAsc(MenuDo::getSort);
        List<MenuDo> button = menuDao.selectList(buttonWra);
        List<RoleGiveMenuInitTree> buttonList = new ArrayList<>();
        for (MenuDo menuDo : button) {
            RoleGiveMenuInitTree tree = new RoleGiveMenuInitTree(menuDo.getId(), menuDo.getUrl(), menuDo.getComponent(), menuDo.getName(), menuDo.getType(), checkStatus(menuDo.getId(), menuSet), menuDo.getIcon(), menuDo.getSystem());
            buttonList.add(tree);
        }
        return buttonList;
    }

    private int checkStatus(Long menuId, Set<Long> menuSet) {
        return menuId != null && menuSet != null && menuSet.contains(menuId) ? 1 : 0;
    }

    //新增校验
    private void addCheck(RoleAddReq req, Long companyId, Long tenantId, Integer roleType) {
        LambdaQueryWrapper<RoleDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoleDo::getRoleName, req.getRoleName())
                .eq(RoleDo::getTenantId, tenantId)
                .eq(RoleDo::getRoleType, roleType)
                .eq(companyId != null, RoleDo::getCompanyId, companyId);
        int count = this.baseMapper.selectCount(wrapper);
        if (count > 0) {
            throw new BizException(SysConstant.Role_AddCheck_Name);
        }
    }


    @Override
    public RoleDetailRes updateInit(Long id, String source) {
        Integer roleType = StaticDict.System_RoleType.getRoleType(source);
        RoleDetailRes res = new RoleDetailRes();
        RoleDo roleDo = dao.selectById(id);
        if (roleDo == null || !Objects.equals(roleType, roleDo.getRoleType())) {
            throw new BizException(SysConstant.No_Data);
        }
        res.setId(roleDo.getId());
        res.setRoleName(roleDo.getRoleName());
        res.setTips(roleDo.getTips());

        List<Integer> menuTypeForInit = StaticDict.System_MenuType.getMenuTypeForInit(source);
        for (Integer menuType : menuTypeForInit) {
            Set<Long> menuIds = this.getAllMenuForAdmin(id, Arrays.asList(menuType));
            if (StaticDict.System_MenuType.Labella_Oms.getValue().equals(menuType)) {
                res.setOmsMenuIds(new ArrayList<>(menuIds));
            } else if (StaticDict.System_MenuType.Labella_Omp.getValue().equals(menuType)) {
                res.setOmpMenuIds(new ArrayList<>(menuIds));
            } else if (StaticDict.System_MenuType.Labella_Wms.getValue().equals(menuType)) {
                res.setWmsMenuIds(new ArrayList<>(menuIds));

            }
        }
        return res;
    }

    public void getMenuIds(List<RoleGiveMenuInitTree> trees, List<Long> menuId) {
        if (trees == null) return;
        for (RoleGiveMenuInitTree tree : trees) {
            if (tree.getStatus() == 1) menuId.add(tree.getId());
            this.getMenuIds(tree.getChildren(), menuId);
        }
    }

    @Override
    public RoleUpdateReq update(RoleUpdateReq req, Long companyId, Long tenantId, String source) {
        Integer roleType = StaticDict.System_RoleType.getRoleType(source);
        RoleDo entityDo = dao.selectById(req.getId());
        if (entityDo == null) {
            throw new BizException(SysConstant.Role_Update_Notfound);
        }
        if (StringUtils.isNotBlank(entityDo.getDevCode())) {
            throw new BizException(SysConstant.NoPower);
        }
        if (!Objects.equals(roleType, entityDo.getRoleType())) {
            throw new BizException(SysConstant.NoPower);
        }
        if (!Objects.equals(tenantId, entityDo.getTenantId())) {
            throw new BizException(SysConstant.NoPower);
        }
        if ("oms".equals(source)) {
            if (!Objects.equals(companyId, entityDo.getCompanyId())) {
                throw new BizException(SysConstant.NoPower);
            }
        }
        updateCheck(req, companyId, tenantId, roleType);
        entityDo.setRoleName(req.getRoleName());
        entityDo.setTips(req.getTips());
        dao.updateById(entityDo);
        //菜单
        this.dealMenu(req, source, entityDo);
        return req;
    }

    //编辑校验
    private void updateCheck(RoleUpdateReq req, Long companyId, Long tenantId, Integer roleType) {
        LambdaQueryWrapper<RoleDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoleDo::getRoleName, req.getRoleName()).ne(RoleDo::getId, req.getId())
                .eq(RoleDo::getRoleType, roleType)
                .eq(companyId != null, RoleDo::getCompanyId, companyId)
                .eq(RoleDo::getTenantId, tenantId);
        int count = this.baseMapper.selectCount(wrapper);
        if (count > 0) {
            throw new BizException(SysConstant.Role_AddCheck_Name);
        }
    }


    @Override
    public int delete(Long id, Long userId, Long companyId, Long tenantId, String source) {
        RoleDo entityDo = dao.selectById(id);
        if ("oms".equals(source)) {
            if (!Objects.equals(entityDo.getCompanyId(), companyId)) return 0;
        } else if ("omp".equals(source)) {
            if (!Objects.equals(entityDo.getTenantId(), tenantId)) return 0;
        }
        deleteCheck(entityDo);
        handlerDeleteBefore(entityDo, userId);
        return dao.deleteById(id);
    }

    private void deleteCheck(RoleDo entityDo) {
        if (StringUtils.isNotBlank(entityDo.getDevCode())) {
            throw new BizException(SysConstant.Role_Delete_Error);
        }

//        LambdaQueryWrapper<RoleMenuRelationDo> wrapper = new LambdaQueryWrapper<>();
//        wrapper.eq(RoleMenuRelationDo::getRoleId, entityDo.getId());
//        int count = roleMenuRelationDao.selectCount(wrapper);
//        if (count > 0) {
//            throw new BizException(SysConstant.Role_Delete_HadChild);
//        }

        LambdaQueryWrapper<UserDo> userDoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userDoLambdaQueryWrapper.eq(UserDo::getRoleId, entityDo.getId());
        int userCount = userDao.selectCount(userDoLambdaQueryWrapper);
        if (userCount > 0) {
            throw new BizException(SysConstant.Role_Delete_HadUser);
        }
    }

    //做删除之前要执行的业务定制处理
    private void handlerDeleteBefore(RoleDo entityDo, Long userId) {
        entityDo.setDeleteBy(userId);
        entityDo.setDeleteTime(new Date());
        this.baseMapper.updateById(entityDo);
    }


    @Override
    public PageResult getListPage(Map<String, Object> map) {
        map.put("roleType", StaticDict.System_RoleType.getRoleType(map.get("source").toString()));
        IPage<RolePageVo> page = pageInit(map);
        page = dao.list(page, map);
        PageResult result = new PageResult(page.getRecords(), (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
        return result;
    }

    @Override
    public List<RolePageVo> getList(Long companyId, Long tenantId, String source) {
        Map<String, Object> map = new HashMap<>();
        map.put("roleType", StaticDict.System_RoleType.getRoleType(source));
        map.put("companyId", companyId);
        map.put("tenantId", tenantId);
        List<RolePageVo> list = dao.list(map);
        return list;
    }


    @Override
    public List<MenuNewTreeRes> getMenuTree(Long roleId, Integer system, Long companyId) {
        List<MenuNewTreeRes> treeResList = new ArrayList<>();
        Set<Long> menuIdSet = this.getAllMenuForAdmin(roleId, Collections.singletonList(system));
        LambdaQueryWrapper<MenuDo> menuWrapper = new LambdaQueryWrapper<>();
        menuWrapper.eq(MenuDo::getParentId, 0L).eq(MenuDo::getType, 1).eq(MenuDo::getSystem, system).orderByAsc(MenuDo::getSort);
        List<MenuDo> menuDoList = menuDao.selectList(menuWrapper);
        for (MenuDo menuDo : menuDoList) {
            if (!menuIdSet.contains(menuDo.getId())) {
                continue;
            }
            MenuNewTreeRes res = new MenuNewTreeRes(menuDo.getId(), menuDo.getUrl(), menuDo.getComponent(), menuDo.getUrlName(), menuDo.getHidden(), new MetaTreeRes(menuDo.getName(), menuDo.getIcon()));
            res.setChildren(queryFindChildren(menuDo.getId(), menuIdSet, 2, system));//筛选菜单
            treeResList.add(res);
        }
        return treeResList;
    }


    private List<MenuNewTreeRes> queryFindChildren(Long menuId, Set<Long> menuIdSet, Integer type, Integer system) {
        if (menuId == null) return null;
        LambdaQueryWrapper<MenuDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MenuDo::getParentId, menuId).eq(MenuDo::getSystem, system).eq(MenuDo::getType, type).orderByAsc(MenuDo::getSort);
        List<MenuDo> menuDoList = menuDao.selectList(wrapper);
        List<MenuNewTreeRes> treeList = new ArrayList<>();
        for (MenuDo menuDo : menuDoList) {
            if (!menuIdSet.contains(menuDo.getId())) {
                continue;
            }
            MenuNewTreeRes res = new MenuNewTreeRes(menuDo.getId(), menuDo.getUrl(), menuDo.getComponent(), menuDo.getUrlName(), menuDo.getHidden(), new MetaTreeRes(menuDo.getName(), menuDo.getIcon()));
            List<MenuNewTreeRes> children = queryFindChildren(menuDo.getId(), menuIdSet, type, system);
            res.setChildren(children);
            treeList.add(res);
        }
        return treeList;
    }


    private IPage<RolePageVo> pageInit(Map<String, Object> map) {
        IPage<RolePageVo> page = new Page();
        page.setSize(map.get("pageSize") != null ? Long.parseLong(map.get("pageSize").toString()) : 10);
        page.setCurrent(map.get("current") != null ? Long.parseLong(map.get("current").toString()) : 1);
        return page;
    }


    /******************************************************OMP***********************************************************/


    /**
     * 新增客户时 新增管理员角色
     *
     * @return
     */
    @Override
    public Long ompAdminRoleAddForCompany(Long companyId, Long userId, Long tenantId) {
        LambdaQueryWrapper<RoleDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoleDo::getRoleName, "管理员").eq(RoleDo::getRoleType, 1).eq(RoleDo::getCompanyId, companyId);
        int count = this.baseMapper.selectCount(wrapper);
        if (count > 0) {
            throw new BizException(SysConstant.Role_AddCheck_Name);
        }
        RoleDo et = new RoleDo();
        et.setRoleName("管理员");
        et.setDevCode(SysConstant.clientAdmin);
        et.setRoleType(StaticDict.System_RoleType.Labella_New_Cus.getValue());//前台角色
        et.setCreateBy(userId);
        et.setCreateTime(new Date());
        et.setCompanyId(companyId);
        et.setTenantId(tenantId);
        dao.insert(et);
        return et.getId();
    }


    /**
     * omp角色管理新增
     *
     * @param tenantId
     * @param userId
     * @return
     */
    @Override
    public Long ompRoleAddForTenant(RoleOmpAddReq req, Long tenantId, Long userId) {
        LambdaQueryWrapper<RoleDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoleDo::getRoleName, req.getRoleName()).eq(RoleDo::getRoleType, 2).eq(RoleDo::getTenantId, tenantId);
        int count = this.baseMapper.selectCount(wrapper);
        if (count > 0) {
            throw new BizException(SysConstant.Role_AddCheck_Name);
        }
        RoleDo et = new RoleDo();
        et.setRoleType(2);//前台角色
        et.setCreateBy(userId);
        et.setCreateTime(new Date());
        et.setTenantId(tenantId);
        dao.insert(et);
        return et.getId();

    }

}
